/* Copyright (C) 2004 Stefan Bellon <sbellon@sbellon.de>
 *
 * This file is part of RemotePrinterFS.
 *
 * RemotePrinterFS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * RemotePrinterFS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <socklib.h>
#include <inetlib.h>
#include <netdb.h>

#include "config.h"
#include "errors.h"
#include "syslogf.h"
#include "utils.h"

static struct sockaddr_in remote_addr;
static int                socket_type;
static bool               buffer_on_disk;
static int                counter   = 0;

static os_error const *
set_value(char *key, char *value)
{
    if (!strcasecmp(key, "network")) {
        if (!strcasecmp(value, "tcp"))
            socket_type = SOCK_STREAM;
        else if (!strcasecmp(value, "udp"))
            socket_type = SOCK_DGRAM;
        else {
            syslogf(LOG_ERR, "Invalid value for key '%s': %s", key, value);
            return ERR(err_InvalidSpecialField);
        }

    } else if (!strcasecmp(key, "address") || !strcasecmp(key, "host")) {
        struct hostent *hent;
        hent = gethostbyname(value);
        if (!hent) {
            syslogf(LOG_ERR, "Hostname lookup failed for: %s", value);
            return ERR(err_NameLookup);
        }
        memcpy(&(remote_addr.sin_addr), hent->h_addr_list[0], hent->h_length);

    } else if (!strcasecmp(key, "service") || !strcasecmp(key, "port")) {
        if (aredigit(value)) {
            remote_addr.sin_port = htons((short) strtoul(value, 0, 0));
        } else {
            struct servent *se =
              getservbyname(value, socket_type == SOCK_STREAM ? "tcp" : "udp");
            if (!se) {
                syslogf(LOG_ERR, "Invalid value for key '%s': %s", key, value);
                return ERR(err_UnknownService);
            }
            remote_addr.sin_port = se->s_port;
        }

    } else if (!strcasecmp(key, "buffer")) {
        if (!strcasecmp(value, "yes"))
            buffer_on_disk = true;
        else if (!strcasecmp(value, "no"))
            buffer_on_disk = false;
        else {
            syslogf(LOG_ERR, "Invalid value for key '%s': %s", key, value);
            return ERR(err_InvalidSpecialField);
        }
    }

    return NULL;
}

static os_error const *
set_parameters(char *special_field)
{
    /* Set default values */
    memset(&remote_addr, 0, sizeof(remote_addr));
    remote_addr.sin_family      = AF_INET;
    remote_addr.sin_port        = htons(9100);
    remote_addr.sin_addr.s_addr = 0x00000000;
    socket_type                 = SOCK_STREAM;
    buffer_on_disk              = false;

    /* Set user values */
    return parse_special_field(special_field, set_value);
}

static int
jetdirect_connect(void)
{
    int s = socket(AF_INET, socket_type, 0);
    if (s == -1) {
        error = ERR(err_Socket);
        syslogf(LOG_ERR, "%s", error->errmess);
        return -1;
    }

    /* Set remote address. */
    if (connect(s, (struct sockaddr*) &remote_addr, sizeof(remote_addr)) == -1)
    {
        close(s);
        error = ERR(err_RemoteAddress);
        syslogf(LOG_ERR, "%s", error->errmess);
        return -1;
    }

    return s;
}

int
jetdirect_open(char *special_field, int length, int filetype)
{
    UNUSED(length);
    UNUSED(filetype);

    syslogf(LOG_INFO, "Starting print job (%s) ...", special_field);

    error = set_parameters(special_field);
    if (error)
        return -1;

    /* We don't care whether we have to abort later on, just increase
       the job counter. If we abort, then not every job number is used. */
    if (++counter > 999)
        counter = 0;

    return jetdirect_connect();
}

int
jetdirect_write(int s, char *buf, int len)
{
    return socketwrite(s, buf, len);
}

int
jetdirect_close(int s)
{
    syslogf(LOG_INFO, "Print job finished.");

    return close(s);
}

char *
jetdirect_get_spool_filename(char *special_field, int length)
{
    char *filename = NULL;

    syslogf(LOG_DEBUG, "Spool filename requested (%s) ...", special_field);
    syslogf(LOG_DEBUG, "File length to print is %i bytes.", length);

    set_parameters(special_field);

    if (buffer_on_disk == true) {
        filename = malloc(7);
        if (!filename)
            syslogf(LOG_CRIT, "malloc failed, no memory allocated");
        sprintf(filename, "JD%03i", counter);
        syslogf(LOG_DEBUG, "Spool filename is \"%s\".", filename);
    }
    
    return filename;
}
